package com.es.service.impl;

import com.alibaba.fastjson.JSON;
import com.es.config.EsConfig;
import com.es.dto.request.UserSearchReq;
import com.es.entity.User;
import com.es.repository.UserRepository;
import com.es.service.UserService;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.index.query.*;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.aggregations.AggregationBuilder;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.util.List;
import java.util.Map;


@Slf4j
@Service
public class UserServiceImpl implements UserService {

    private final UserRepository userRepository;
    private final EsConfig client;

    public UserServiceImpl(UserRepository userRepository, EsConfig client) {
        this.userRepository = userRepository;
        this.client = client;
    }


    @Override
    public Map<String, Integer> groupByGender() {
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        AggregationBuilder aggregationBuilder = AggregationBuilders.terms("byGender").field("gender");
        searchSourceBuilder.aggregation(aggregationBuilder);
        SearchRequest request = new SearchRequest("myuser");
        request.source(searchSourceBuilder);
        Map<String, Integer> map = Maps.newHashMap();
        try {
            SearchResponse search = client.elasticsearchClient().search(request, RequestOptions.DEFAULT);
            ParsedLongTerms byGender = search.getAggregations().get("byGender");
            for (Terms.Bucket b : byGender.getBuckets()) {
                map.put(b.getKeyAsString(), (int) b.getDocCount());
            }
            System.out.println(JSON.toJSONString(map));
        } catch (IOException e) {
            e.printStackTrace();
        }
        return map;
    }

    @Override
    public List<User> findByCar(UserSearchReq searchReq) {
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        // es官方推荐使用get+body方式查询
        sourceBuilder.query(boolQuery(searchReq));
        // 注意：如果不是默认排序（按_score排序），返回字段score会为null
        //sourceBuilder.sort("id", SortOrder.DESC);
        // 可以指定source字段返回的内容
        //sourceBuilder.fetchSource(new String[]{"id", "name", "age", "gender", "car"}, null);
        SearchRequest request = new SearchRequest("myuser");
        request.source(sourceBuilder);

        List<User> list = Lists.newArrayList();
        try {
            // 打印dsl
            log.debug("dsl:{}", request.source().toString());
            SearchResponse search = client.elasticsearchClient().search(request, RequestOptions.DEFAULT);
            System.out.println(JSON.toJSONString(search));
            for (SearchHit hit : search.getHits().getHits()) {
                Map<String, Object> sourceAsMap = hit.getSourceAsMap();
                User u = new User();
                u.setId(sourceAsMap.get("id").toString());
                u.setName(sourceAsMap.get("name").toString());
                u.setAge((Integer) sourceAsMap.get("age"));
                u.setGender((Integer) sourceAsMap.get("gender"));
                u.setCar(sourceAsMap.get("car").toString());
                list.add(u);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return list;
    }

    /**
     * 常用条件demo
     * should = or, must = and, mustNot = not
     * query: 会返回score值，更关注文档匹配程度，当文档匹配时，还会计算匹配分数
     * filter: score值为0，更直接地返回文档是否匹配，一般情况下更应该使用filter，es可以缓存filter查询，效率更高
     *
     * @param searchReq 查询条件
     */
    private BoolQueryBuilder boolQuery(UserSearchReq searchReq) {
        BoolQueryBuilder filterQuery = QueryBuilders.boolQuery();
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        // 以下条件等价于: AND (car LIKE '%1%' OR name = 2 OR age = 3 OR gender = 4)
        if (!StringUtils.isEmpty(searchReq.getCar())) {
            boolQuery.should(new MatchQueryBuilder("car", searchReq.getCar()));
        }
        if (!StringUtils.isEmpty(searchReq.getName())) {
            boolQuery.should(new TermQueryBuilder("name", searchReq.getName()));
        }
        if (searchReq.getAge() != null) {
            boolQuery.should(new TermQueryBuilder("age", searchReq.getAge()));
        }
        if (searchReq.getGender() != null) {
            boolQuery.should(new TermQueryBuilder("gender", searchReq.getGender()));
        }
        // -----------------------

        // 以下条件等价于: AND car LIKE '%1%' AND (age = 2 OR gender = 3)
        if (!StringUtils.isEmpty(searchReq.getCar())) {
            boolQuery.must(new MatchQueryBuilder("car", searchReq.getCar()));
        }
        if (searchReq.getAge() != null && searchReq.getGender() != null) {
            BoolQueryBuilder orBuilder = QueryBuilders.boolQuery();
            orBuilder.should(new TermQueryBuilder("age", searchReq.getAge()));
            orBuilder.should(new TermQueryBuilder("gender", searchReq.getGender()));
            boolQuery.must(orBuilder);
        }
        // -----------------------

        // 以下条件等价于: AND age > 22 AND age <= 25
        boolQuery.must(new RangeQueryBuilder("age").gt(22).lte(25));

        filterQuery.filter(boolQuery);
        return filterQuery;
    }


}
